package com.fan.plugin;

import com.baomidou.mybatisplus.core.metadata.IPage;
import com.fan.annotation.EncryptedField;
import com.fan.handler.EncryptionHandler;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.executor.resultset.ResultSetHandler;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.SystemMetaObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.Field;
import java.sql.Statement;
import java.util.*;

@Intercepts({
    @Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class}),
    @Signature(type = ResultSetHandler.class, method = "handleResultSets", args = {Statement.class})
})
public class EncryptionInterceptor implements Interceptor {
    
    private static final Logger log = LoggerFactory.getLogger(EncryptionInterceptor.class);
    
    // 使用 ThreadLocal 保存原始值，用于在数据库操作完成后恢复
    private static final ThreadLocal<Map<Object, Map<Field, String>>> ORIGINAL_VALUES = new ThreadLocal<>();
    
    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        Object target = invocation.getTarget();
        
        // 处理 INSERT 和 UPDATE
        if (target instanceof Executor) {
            return handleUpdate(invocation);
        }
        
        // 处理 SELECT
        if (target instanceof ResultSetHandler) {
            return handleQuery(invocation);
        }
        
        return invocation.proceed();
    }
    
    /**
     * 处理 INSERT 和 UPDATE 操作
     */
    private Object handleUpdate(Invocation invocation) throws Throwable {
        Object[] args = invocation.getArgs();
        MappedStatement ms = (MappedStatement) args[0];
        Object parameter = args[1];
        
        SqlCommandType sqlCommandType = ms.getSqlCommandType();
        
        // 只处理 INSERT 和 UPDATE
        if (sqlCommandType == SqlCommandType.INSERT || sqlCommandType == SqlCommandType.UPDATE) {
            if (parameter != null) {
                // 初始化 ThreadLocal
                ORIGINAL_VALUES.set(new HashMap<>());
                
                try {
                    // 处理单个对象
                    if (isEntityObject(parameter)) {
                        encryptAndSaveOriginal(parameter);
                        log.debug("执行加密: {}, 操作类型: {}", parameter.getClass().getSimpleName(), sqlCommandType);
                    }
                    // 处理集合（批量插入/更新）
                    else if (parameter instanceof Map) {
                        Map<?, ?> map = (Map<?, ?>) parameter;
                        for (Object value : map.values()) {
                            if (value instanceof Collection) {
                                Collection<?> collection = (Collection<?>) value;
                                for (Object item : collection) {
                                    if (isEntityObject(item)) {
                                        encryptAndSaveOriginal(item);
                                    }
                                }
                            } else if (isEntityObject(value)) {
                                encryptAndSaveOriginal(value);
                            }
                        }
                    }
                    
                    // 执行数据库操作
                    Object result = invocation.proceed();
                    
                    // 恢复原始值
                    restoreOriginalValues();
                    
                    return result;
                    
                } finally {
                    // 清理 ThreadLocal
                    ORIGINAL_VALUES.remove();
                }
            }
        }
        
        return invocation.proceed();
    }
    
    /**
     * 处理 SELECT 查询结果
     */
    private Object handleQuery(Invocation invocation) throws Throwable {
        Object result = invocation.proceed();
        
        if (result == null) {
            return null;
        }

        // 处理结果集
        if (result instanceof List) {
            List<?> list = (List<?>) result;
            if (!list.isEmpty()) {
                Object firstItem = list.get(0);
                if (isEntityObject(firstItem) && EncryptionHandler.hasEncryptedFields(firstItem.getClass())) {
                    for (Object item : list) {
                        EncryptionHandler.decrypt(item);
                    }
                    log.debug("执行解密: {} 条记录", list.size());
                }
            }
        } else if (result instanceof Map) {
            // 处理Map类型的结果（可能是分页查询结果）
            Map<?, ?> resultMap = (Map<?, ?>) result;
            if (resultMap.containsKey("records")) {
                Object records = resultMap.get("records");
                if (records instanceof List) {
                    List<?> recordList = (List<?>) records;
                    if (!recordList.isEmpty()) {
                        Object firstItem = recordList.get(0);
                        if (isEntityObject(firstItem) && EncryptionHandler.hasEncryptedFields(firstItem.getClass())) {
                            for (Object item : recordList) {
                                EncryptionHandler.decrypt(item);
                            }
                            log.debug("执行解密: {} 条记录", recordList.size());
                        }
                    }
                }
            }
        } else if (isEntityObject(result) && EncryptionHandler.hasEncryptedFields(result.getClass())) {
            EncryptionHandler.decrypt(result);
            log.debug("执行解密: 单条记录");
        }

        return result;
    }
    
    /**
     * 判断是否是实体对象（排除基本类型和Map等）
     */
    private boolean isEntityObject(Object obj) {
        if (obj == null) {
            return false;
        }
        
        Class<?> clazz = obj.getClass();
        
        // 排除基本类型、包装类、String、Map、Collection等
        return !clazz.isPrimitive()
            && !clazz.getName().startsWith("java.")
            && !(obj instanceof Map)
            && !(obj instanceof Collection);
    }
    
    @Override
    public Object plugin(Object target) {
        return Plugin.wrap(target, this);
    }
    
    @Override
    public void setProperties(Properties properties) {
        // 可以通过配置文件设置属性
    }
    
    /**
     * 加密并保存原始值
     */
    private void encryptAndSaveOriginal(Object entity) {
        if (entity == null) {
            return;
        }
        
        try {
            Class<?> clazz = entity.getClass();
            Field[] fields = clazz.getDeclaredFields();
            Map<Field, String> entityOriginalValues = new HashMap<>();
            
            // 第一步：保存所有需要加密字段的原始值
            for (Field field : fields) {
                if (field.getAnnotation(EncryptedField.class) == null) {
                    continue;
                }
                
                field.setAccessible(true);
                String plainText = (String) field.get(entity);
                
                // 如果字段为空或已经加密，跳过
                if (plainText == null || plainText.trim().isEmpty()) {
                    continue;
                }
                
                // 检查是否已经加密
                if (plainText.startsWith("ENC(")) {
                    continue;
                }
                
                // 保存原始值
                entityOriginalValues.put(field, plainText);
            }
            
            // 第二步：如果有需要加密的字段，执行加密
            if (!entityOriginalValues.isEmpty()) {
                EncryptionHandler.encrypt(entity);
                // 保存到 ThreadLocal
                ORIGINAL_VALUES.get().put(entity, entityOriginalValues);
            }
            
        } catch (Exception e) {
            log.error("加密并保存原始值失败", e);
        }
    }
    
    /**
     * 恢复原始值
     */
    private void restoreOriginalValues() {
        Map<Object, Map<Field, String>> allOriginalValues = ORIGINAL_VALUES.get();
        if (allOriginalValues == null || allOriginalValues.isEmpty()) {
            return;
        }
        
        try {
            for (Map.Entry<Object, Map<Field, String>> entityEntry : allOriginalValues.entrySet()) {
                Object entity = entityEntry.getKey();
                Map<Field, String> fieldValues = entityEntry.getValue();
                
                for (Map.Entry<Field, String> fieldEntry : fieldValues.entrySet()) {
                    Field field = fieldEntry.getKey();
                    String originalValue = fieldEntry.getValue();
                    
                    field.setAccessible(true);
                    field.set(entity, originalValue);
                }
                
                log.debug("恢复对象 [{}] 的原始值", entity.getClass().getSimpleName());
            }
        } catch (Exception e) {
            log.error("恢复原始值失败", e);
        }
    }
}